home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mntlib43 / mntlib / stat.c < prev    next >
C/C++ Source or Header  |  1993-10-20  |  8KB  |  333 lines

  1. /*
  2.  * stat, fstat, lstat emulation for TOS
  3.  * written by Eric R. Smith and placed in the public domain
  4.  */
  5.  
  6. #include <limits.h>
  7. #include <types.h>
  8. #include <stat.h>
  9. #include <ctype.h>
  10. #include <errno.h>
  11. #include <osbind.h>
  12. #include <mintbind.h>
  13. #include <string.h>
  14. #include <time.h>
  15. #include <unistd.h>
  16. #include <support.h>
  17. #include <ioctl.h>    /* for FSTAT */
  18. #include "lib.h"
  19.  
  20. extern int __mint;
  21.  
  22. ino_t    __inode = 32;        /* used in readdir also */
  23.  
  24. /* for backwards compatibilty: if nonzero, files are checked to see if
  25.  * they have the TOS executable magic number in them
  26.  */
  27.  
  28. int    _x_Bit_set_in_stat = 0;
  29.  
  30.  
  31.  
  32. /* date for files (like root directories) that don't have one */
  33. #define OLDDATE _unixtime(0,0)
  34.  
  35. /*
  36.  * macro for converting a long in DOS format to one in Unix format. "x"
  37.  * _must_ be an lvalue!
  38.  */
  39.  
  40. #define CONVERT(x) (x = _unixtime( ((short *)&x)[0], ((short *)&x)[1] ))
  41.  
  42. /*
  43.  * common routine for stat() and lstat(); if "lflag" is 0, then symbolic
  44.  * links are automatically followed (like stat), if 1 then they are not
  45.  * (like lstat)
  46.  */
  47.  
  48. static int do_stat __PROTO((const char *_path, struct stat *st, 
  49.     int lflag));
  50.  
  51. static int
  52. do_stat(_path, st, lflag)
  53.     const char *_path;
  54.     struct stat *st;
  55.     int lflag;
  56. {
  57.     long    r;
  58.     _DTA    *olddta;
  59.     int    nval;
  60.     char    path[PATH_MAX];
  61.     char    *ext, drv;
  62.     int    fd;
  63.     short    magic;
  64.     _DTA    d;
  65.     int    isdot = 0;
  66.  
  67.     if (!_path) {
  68.         errno = EFAULT;
  69.         return -1;
  70.     }
  71.  
  72. /*
  73.  * _unx2dos returns 1 for device names (like /dev/con)
  74.  */
  75.     nval = _unx2dos(_path, path);
  76.  
  77. /* for MiNT 0.9 and up, we use the built in stat() call */
  78.     if (__mint >= 9) {
  79.         r = Fxattr(lflag, path, st);
  80.         if (r) {
  81.             errno = (int) -r;
  82.             return -1;
  83.         }
  84.         CONVERT(st->st_mtime);
  85.         CONVERT(st->st_atime);
  86.         CONVERT(st->st_ctime);
  87.     /* Most versions of Unix count in 512 byte blocks */
  88.         st->st_blocks = (st->st_blocks * st->st_blksize) / 512;
  89.     /* if we hit a symbolic link, try to get its size right */
  90.         if (lflag && ((st->st_mode & S_IFMT) == S_IFLNK)) {
  91.             char buf[PATH_MAX + 1];
  92.             char buf1[PATH_MAX + 1];
  93.             r = Freadlink(PATH_MAX, buf, path);
  94.             if (r < 0)
  95.               {
  96.                 errno = (int) -r;
  97.                 return -1;
  98.               }
  99.             buf[PATH_MAX] = 0;
  100.             _dos2unx (buf, buf1);
  101.             st->st_size = strlen (buf1);
  102.         }
  103.         return 0;
  104.     }
  105.  
  106. /* otherwise, check to see if we have a name like CON: or AUX: */
  107.     if (nval == 1) {
  108.         st->st_mode = S_IFCHR | 0600;
  109.         st->st_attr = 0;
  110.         st->st_ino = ++__inode;
  111.         st->st_rdev = 0;
  112.         st->st_mtime = st->st_ctime = st->st_atime = 
  113.             time((time_t *)0) - 2;
  114.         st->st_dev = 0;
  115.         st->st_nlink = 1;
  116.         st->st_uid = geteuid();
  117.         st->st_gid = getegid();
  118.         st->st_size = st->st_blocks = 0;
  119.         st->st_blksize = 1024;
  120.         return 0;
  121.     }
  122.  
  123. /* A file name: check for root directory of a drive */
  124.     if (path[0] == '\\' && path[1] == 0) {
  125.         drv = Dgetdrv() + 'A';
  126.         goto rootdir;
  127.     }
  128.  
  129.     if ( ((drv = path[0]) != 0) && path[1] == ':' &&
  130.          (path[2] == 0 || (path[2] == '\\' && path[3] == 0)) ) {
  131. rootdir:
  132.         st->st_mode = S_IFDIR | 0755;
  133.         st->st_attr = FA_DIR;
  134.         st->st_dev = isupper(drv) ? drv - 'A' : drv - 'a';
  135.         st->st_ino = 2;
  136.         st->st_mtime = st->st_ctime = st->st_atime = OLDDATE;
  137.         goto fill_dir;
  138.     }
  139.  
  140. /* forbid wildcards in path names */
  141.     if (index(path, '*') || index(path, '?')) {
  142.         errno = ENOENT;
  143.         return -1;
  144.     }
  145.  
  146. /* OK, here we're going to have to do an Fsfirst to get the date */
  147. /* NOTE: Fsfirst(".",-1) or Fsfirst("..",-1) both fail under TOS,
  148.  * so we kludge around this by using the fact that Fsfirst(".\*.*"
  149.  * or "..\*.*" will return the correct file first (except, of course,
  150.  * in root directories :-( ).
  151.  * NOTE2: Some versions of TOS don't like Fsfirst("RCS\\", -1) either,
  152.  * so we do the same thing if the path ends in '\\'.
  153.  */
  154.  
  155. /* find the end of the string */
  156.     for (ext = path; ext[0] && ext[1]; ext++) ;
  157.  
  158. /* add appropriate kludge if necessary */
  159.     if (*ext == '.' && (ext == path || ext[-1] == '\\' || ext[-1] == '.')) {
  160.         isdot = 1;
  161.         strcat(path, "\\*.*");
  162.     } else if (*ext == '\\') {
  163.         isdot = 1;
  164.         strcat(path, "*.*");
  165.     }
  166.     olddta = Fgetdta();
  167.     Fsetdta(&d);
  168.     r = Fsfirst(path, 0xff);
  169.     Fsetdta(olddta);
  170.     if (r < 0) {
  171.         if (isdot && r == -ENOENT) goto rootdir;
  172.         errno = (int) -r;
  173.         return -1;
  174.     }    
  175.  
  176.     if (isdot && ((d.dta_name[0] != '.') || (d.dta_name[1]))) {
  177.         goto rootdir;
  178.     }
  179.  
  180.     st->st_mtime = st->st_ctime = st->st_atime =
  181.         _unixtime(d.dta_time, d.dta_date);
  182.     if (((drv = *path) != 0) && path[1] == ':')
  183.         st->st_dev = toupper(drv) - 'A';
  184.     else
  185.         st->st_dev = Dgetdrv();
  186.  
  187.     st->st_ino = __inode++;
  188.     st->st_attr = d.dta_attribute;
  189.     if (__mint && st->st_dev == ('Q' - 'A'))
  190.             st->st_mode = 0644 | S_IFIFO;
  191.     else {
  192.         st->st_mode = 0644 | (st->st_attr & FA_DIR ?
  193.                   S_IFDIR | 0111 : S_IFREG);
  194.     }
  195.  
  196.     if (st->st_attr & FA_RDONLY)
  197.         st->st_mode &= ~0222;    /* no write permission */
  198.     if (st->st_attr & FA_HIDDEN)
  199.         st->st_mode &= ~0444;    /* no read permission */
  200.  
  201. /* check for a file with an executable extension */
  202.     ext = strrchr(_path, '.');
  203.     if (ext) {
  204.         if (!strcmp(ext, ".ttp") || !strcmp(ext, ".prg") ||
  205.             !strcmp(ext, ".tos") || !strcmp(ext, ".g") ||
  206.             !strcmp(ext, ".sh")     || !strcmp(ext, ".bat")) {
  207.             st->st_mode |= 0111;
  208.         }
  209.     }
  210.     if ( (st->st_mode & S_IFMT) == S_IFREG) {
  211.         if (_x_Bit_set_in_stat) {
  212.             if ((fd = (int) Fopen(path,0)) < 0) {
  213.                 errno = -fd;
  214.                 return -1;
  215.             }
  216.             magic = 0;
  217.             (void)Fread(fd,2,(char *)&magic);
  218.             (void)Fclose(fd);
  219.             if (magic == 0x601A    /* TOS executable */
  220.                 || magic == 0x2321) /* "#!" shell file */
  221.                 st->st_mode |= 0111;
  222.         }
  223.         st->st_size = d.dta_size;
  224.     /* in Unix, blocks are measured in 512 bytes */
  225.         st->st_blocks = (st->st_size + 511) / 512;
  226.         st->st_nlink = 1; /* we dont have hard links */
  227.     } else {
  228. fill_dir:
  229.         st->st_size = 1024;
  230.         st->st_blocks = 2;
  231.         st->st_nlink = 2;    /* "foo" && "foo/.." */
  232.     }
  233.  
  234.     st->st_rdev = 0;
  235.     st->st_uid = geteuid();    /* the current user owns every file */
  236.     st->st_gid = getegid();
  237.     st->st_blksize = 1024;
  238.     return 0;
  239. }
  240.  
  241.  
  242. /* 
  243.  * fstat: if we're not running under MiNT, this is pretty bogus.
  244.  * what we can really find is:
  245.  * modification time: via Fdatime()
  246.  * file size: via Fseek()
  247.  * fortunately, these are the things most programs are interested in.
  248.  * BUG: passing an invalid file descriptor gets back a stat structure for
  249.  * a tty.
  250.  */
  251.  
  252. int
  253. fstat(fd, st)
  254. int fd;
  255. struct stat *st;
  256. {
  257.     long oldplace, r;
  258.     _DOSTIME timeptr;
  259.     short magic;
  260.  
  261.     if (__mint >= 9) {        /* use FSTAT Fcntl */
  262.         r = Fcntl(fd, (long)st, FSTAT);
  263.         if (r) {
  264.             errno = (int) -r;
  265.             return -1;
  266.         }
  267.         CONVERT(st->st_mtime);
  268.         CONVERT(st->st_atime);
  269.         CONVERT(st->st_ctime);
  270.         st->st_blocks = (st->st_blocks * st->st_blksize) / 512;
  271.         return 0;
  272.     }
  273.  
  274.     r = Fdatime(&timeptr, fd, 0);
  275.     if (r < 0) {            /* assume TTY */
  276.         st->st_mode = S_IFCHR | 0600;
  277.         st->st_attr = 0;
  278.         st->st_mtime = st->st_ctime = st->st_atime =
  279.             time((time_t *)0) - 2;
  280.         st->st_size = 0;
  281.     } else {
  282.         st->st_mtime = st->st_atime = st->st_ctime =
  283.             _unixtime(timeptr.time, timeptr.date);
  284.         st->st_mode = S_IFREG | 0644;        /* this may be false */
  285.         st->st_attr = 0;            /* because this is */
  286.  
  287.     /* get current file location */
  288.         oldplace = Fseek(0L, fd, SEEK_CUR);
  289.         if (oldplace < 0) {        /* can't seek -- must be pipe */
  290.             st->st_mode = S_IFIFO | 0644;
  291.             st->st_size = 1024;
  292.         } else {
  293.             r = Fseek(0L, fd, SEEK_END);    /* go to end of file */
  294.             st->st_size = r;
  295.             (void)Fseek(0L, fd, SEEK_SET);    /* go to start of file */
  296.             /* check for executable file */
  297.             if (Fread(fd, 2, (char *)&magic) == 2) {
  298.                 if (magic == 0x601a || magic == 0x2321)
  299.                     st->st_mode |= 0111;
  300.             }
  301.             (void)Fseek(oldplace, fd, SEEK_SET);
  302.         }
  303.     }
  304.  
  305. /* all this stuff is likely bogus as well. sigh. */
  306.     st->st_dev = Dgetdrv();
  307.     st->st_rdev = 0;
  308.     st->st_uid = getuid();
  309.     st->st_gid = getgid();
  310.     st->st_blksize = 1024;
  311. /* note: most Unixes measure st_blocks in 512 byte units */
  312.     st->st_blocks = (st->st_size + 511) / 512;
  313.     st->st_ino = ++__inode;
  314.     st->st_nlink = 1;
  315.     return 0;
  316. }
  317.  
  318. int
  319. lstat(path, st)
  320.     const char *path;
  321.     struct stat *st;
  322. {
  323.     return do_stat(path, st, 1);
  324. }
  325.  
  326. int
  327. stat(path, st)
  328.     const char *path;
  329.     struct stat *st;
  330. {
  331.     return do_stat(path, st, 0);
  332. }
  333.